#include <gtest/gtest.h>
#include "../vas_algorithm.h"

using namespace vas;

TEST(AlgorithmTest, BuildAlgorithmData) {
    cv::Mat img(300, 200, CV_32FC3, cv::Scalar(0, 0, 255));
    Task task1;
    task1.type = FIRE_DETECTION_TASK;
    task1.interval = 10;
    Task task2;
    task2.type = HUMAN_DETECTION_TASK;
    task2.interval = 10;
    Channel channel;
    channel.channel = "1";
    channel.tasks.emplace_back(task1);
    channel.tasks.emplace_back(task2);
    AlgorithmData* data = BuildAlgorithmData(img, channel, 1);
    ASSERT_GT(data->routes.size(), 0);
    ASSERT_EQ(data->traces.size(), 0);
    ASSERT_EQ(data->image.cols, img.cols);
    ASSERT_EQ(data->image.rows, img.rows);
    delete data;
}

TEST(AlgorithmTest, RouteAlgorithm) {
    std::map<std::string, bool> algo_mock;
    algo_mock.insert(std::pair<std::string, bool>(FIRE_RECOGNITION_ALGO, true));
    algo_mock.insert(std::pair<std::string, bool>(HUMAN_DETECTION_ALGO, true));
    auto *data = new AlgorithmData();
    data->image = cv::Mat(300, 200, CV_32FC3, cv::Scalar(0, 0, 255));
    data->routes = {FIRE_RECOGNITION_ALGO, HUMAN_DETECTION_ALGO, SEND_MSG};
    ASSERT_NO_THROW(RouteAlgorithm(data, algo_mock));
    ASSERT_NO_THROW(RouteAlgorithm(data, algo_mock));
    ASSERT_NO_THROW(RouteAlgorithm(data, algo_mock));
    ASSERT_EQ(data->routes.size(), data->traces.size());
    delete data;
    auto *wrong_data = new AlgorithmData();
    wrong_data->image = cv::Mat(300, 200, CV_32FC3, cv::Scalar(0, 0, 255));
    wrong_data->routes = {"WRONG_ALGO"};
    ASSERT_THROW(RouteAlgorithm(wrong_data, algo_mock), VasNotFound);
}

TEST(AlgorithmTest, FireRecognition) {
    ASSERT_NO_THROW(FireRecognitionInit(true));
    ASSERT_NO_THROW(FireRecognitionInit(false));
    auto *data = new AlgorithmData();
    data->image = cv::Mat(300, 200, CV_32FC3, cv::Scalar(0, 0, 255));
    ASSERT_NO_THROW(FireRecognitionProcess(data, true));
    ASSERT_NO_THROW(FireRecognitionProcess(data, false));
    delete data;
}

TEST(AlgorithmTest, HumanDetection) {
    ASSERT_NO_THROW(HumanDetectionInit(true));
    ASSERT_NO_THROW(HumanDetectionInit(false));
    auto *data = new AlgorithmData();
    data->image = cv::Mat(300, 200, CV_32FC3, cv::Scalar(0, 0, 255));
    ASSERT_NO_THROW(HumanDetectionProcess(data, true));
    ASSERT_NO_THROW(FireRecognitionProcess(data, false));
    delete data;
}

TEST(AlgorithmTest, ConvertAlgorithmResult) {
    Channel channel;
    channel.channel = "test_c";
    channel.url = "test_c_url";

    Task task1;
    task1.type = HUMAN_DETECTION_TASK;
    task1.interval = 5;
    channel.tasks.emplace_back(task1);

    Task task2;
    task2.type = FIRE_DETECTION_TASK;
    task2.interval = 5;
    channel.tasks.emplace_back(task2);

    auto *data = new AlgorithmData();
    data->image = cv::Mat(300, 200, CV_32FC3, cv::Scalar(0, 0, 255));
    data->timestamp = 1;
    data->channel = channel;

    data->scenes.insert(FIRE_LABELS[0]);

    PedestrianData ped;
    ped.box.top_left.x = 0;
    ped.box.top_left.y = 0;
    ped.box.bottom_right.x = 1;
    ped.box.bottom_right.y = 1;

    data->pedestrians.emplace_back(ped);

    std::string res_str = ConvertAlgorithmResult(data, {HUMAN_DETECTION_TASK, FIRE_DETECTION_TASK});
    ASSERT_GT(res_str.size(), 0);

    Json::Value res = StringToJson(res_str);
    ASSERT_EQ(res["channel"].asString(), channel.channel);
    std::string img_str = res["image"].asString();
    cv::Mat img = Base64ToMat(img_str);
    ASSERT_EQ(img.cols, data->image.cols);
    ASSERT_EQ(img.rows, data->image.rows);
    ASSERT_EQ(res["timestamp"].asInt(), data->timestamp);
    ASSERT_EQ(res["tasks"].size(), channel.tasks.size());
    for (auto &i : res["tasks"]) {
        if (i["type"] == FIRE_DETECTION_TASK) {
            ASSERT_EQ(i["result"].size(), 1);
            ASSERT_EQ(i["result"][0]["label"].asString(), "NG");
            ASSERT_EQ(i["result"][0]["coordinate"].size(), 4);
            ASSERT_EQ(i["result"][0]["coordinate"][0].asInt(), 0);
            ASSERT_EQ(i["result"][0]["coordinate"][1].asInt(), 0);
            ASSERT_EQ(i["result"][0]["coordinate"][2].asInt(), data->image.cols);
            ASSERT_EQ(i["result"][0]["coordinate"][3].asInt(), data->image.rows);
        } else if (i["type"] == HUMAN_DETECTION_TASK) {
            ASSERT_EQ(i["result"].size(), 1);
            ASSERT_EQ(i["result"][0]["label"].asString(), "NG");
            ASSERT_EQ(i["result"][0]["coordinate"].size(), 4);
            ASSERT_EQ(i["result"][0]["coordinate"][0].asInt(), ped.box.top_left.x);
            ASSERT_EQ(i["result"][0]["coordinate"][1].asInt(), ped.box.top_left.y);
            ASSERT_EQ(i["result"][0]["coordinate"][2].asInt(), ped.box.bottom_right.x);
            ASSERT_EQ(i["result"][0]["coordinate"][3].asInt(), ped.box.bottom_right.y);
        } else {
            throw std::runtime_error("illegal task type");
        }
    }
    delete data;
}